package de.ret_world.openrpgstudio.mapeditor;

import buoy.event.RepaintEvent;
import buoy.event.WidgetMouseEvent;
import buoy.widget.BOutline;
import buoy.widget.BScrollPane;
import buoy.widget.BorderContainer;
import buoy.widget.CustomWidget;
import de.ret_world.openrpgstudio.editor.Project;
import de.ret_world.openrpgstudio.mapeditor.scripts.TileSet;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import javax.imageio.ImageIO;
import org.apache.log4j.Logger;

/**
 * The TileSetWidget is similar to the ImagePreviewWidget but has functions to show a grid and react to mouse clicks.
 *
 * @author Emu
 */
public class TileSetWidget extends BorderContainer {

    private boolean enabled = true;
    private BScrollPane scrl;
    private CustomWidget pane;
    private ArrayList<Tile> tiles;
    private HashSet<Tile> selectedTiles;
    private int width, height = 0;
    private TileSet tileset;
    private CalcThread calcThread;
    //Flags
    private int gap = 2;
    private boolean showNumbers = false;

    public TileSetWidget(TileSet tileset) {
        this();
        setTileSet(tileset);
    }

    public TileSetWidget() {
        tiles = new ArrayList<>();
        selectedTiles = new HashSet<>();

        pane = new CustomWidget();
        pane.addEventLink(RepaintEvent.class, this, "paintIt");
        pane.addEventLink(WidgetMouseEvent.class, this, "dispachMouseEvents");
        scrl = new BScrollPane(pane);
        add(BOutline.createBevelBorder(scrl, false), CENTER);
    }

    public void setGap(int gap) {
        this.gap = gap;
    }

    public int getGap() {
        return gap;
    }

    public boolean isShowNumbers() {
        return showNumbers;
    }

    public void setTileSet(TileSet tileset) {
        if ((this.tileset == null || !this.tileset.equals(tileset))
                && (tileset != null && tileset.getTiles() != null)) {
            this.tileset = tileset;
            if (calcThread != null) {
                calcThread.stop = true;
            }
            calcThread = new CalcThread();
            calcThread.tileset = tileset;
            calcThread.start();
        }
    }

    private void paintIt(RepaintEvent ev) {
        Graphics2D g = ev.getGraphics();

        if (!tiles.isEmpty() && isEnabled()) {
            for (int i = 0; i < tiles.size(); i++) {
                Tile t = tiles.get(i);
                Rectangle r = t.getBounds();
                g.drawImage(t.getImage(), r.x, r.y, r.width, r.height, null);

                if (showNumbers) {
                    g.setFont(new Font("Arial", Font.BOLD, 12));
                    g.setColor(Color.white);
                    g.drawString("" + i, r.x + 3, r.y + 13);
                }

                if (t.selected) {
                    g.setColor(new Color(255, 255, 0, 96));
                    g.fillRect(r.x, r.y, r.width, r.height);
                }
            }

            pane.setPreferredSize(new Dimension(width, height));
            scrl.layoutChildren();
        }
    }

    private void dispachMouseEvents(WidgetMouseEvent ev) {
        dispatchEvent(ev);
    }

    @Override
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        pane.repaint();
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void setShowNumbers(boolean showNumbers) {
        this.showNumbers = showNumbers;
        pane.repaint();
    }

    public void selectTile(Point p) {
        try {
            for (Tile t : tiles) {
                if (t.bounds.contains(p.x, p.y)) {
                    t.selected = true;
                    selectedTiles.add(t);
                }
            }
        } catch (ConcurrentModificationException ex) {
        }
        pane.repaint();
    }

    public void deselectTiles() {
        for (Tile t : selectedTiles) {
            t.selected = false;
        }
        selectedTiles.clear();
        pane.repaint();
    }

    public int getSelectedTilesWidth() {
        if (selectedTiles.isEmpty()) {
            return 0;
        }

        int w = 1;

        ArrayList<Integer> stiles = getSelectedTiles();
        int tmp = stiles.get(0);
        for (Integer i : stiles) {
            if (tmp != i) {
                if (tmp + 1 == i) {
                    w++;
                    tmp = i;
                } else {
                    break;
                }
            }
        }

        return w;
    }

    public ArrayList<Integer> getSelectedTiles() {
        ArrayList<Integer> lst = new ArrayList<>();
        for (Tile t : selectedTiles) {
            lst.add(t.index);
        }
        Collections.sort(lst);
        return lst;
    }

    private class Tile {

        private BufferedImage image;
        private Rectangle bounds;
        protected boolean selected;
        private int index;

        public Tile() {
        }

        public Tile(BufferedImage image, Rectangle bounds, int index) {
            this.image = image;
            this.bounds = bounds;
            this.index = index;
        }

        public Rectangle getBounds() {
            return bounds;
        }

        public void setBounds(Rectangle bounds) {
            this.bounds = bounds;
        }

        public BufferedImage getImage() {
            return image;
        }

        public void setImage(BufferedImage image) {
            this.image = image;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        public int getIndex() {
            return index;
        }
    }

    private class CalcThread extends Thread {

        protected TileSet tileset;
        protected boolean stop = false;

        @Override
        public void run() {
            try {
                if (tileset != null && tileset.getImage() != null) {
                    BufferedImage image = ImageIO.read(Project.getInstance().getFolder("root").resolve(tileset.getImage().getFile()).toFile());

                    int tw = tileset.getTileWidth();
                    int th = tileset.getTileHeight();

                    height = tileset.getTilesPerColumn() * (th + gap) + gap;
                    width = tileset.getTilesPerColumn() * (tw + gap) + gap;

                    tiles.clear();
                    int i = 0;
                    for (int c = 0; c < tileset.getTilesPerColumn(); c++) {
                        for (int r = 0; r < tileset.getTilesPerRow(); r++, i++) {
                            if (tileset.getTiles().size() > i) {
                                Rectangle rect2 = tileset.getTiles().get(i);
                                BufferedImage simg = image.getSubimage(rect2.x, rect2.y, rect2.width, rect2.height);

                                Rectangle rect = new Rectangle(r * (tw + gap) + gap, c * (th + gap) + gap, tw, th);

                                tiles.add(new Tile(simg, rect, i));
                                repaint();
                            }
                            if (stop) {
                                break;
                            }
                        }
                        if (stop) {
                            break;
                        }
                    }
                }
            } catch (IOException ex) {
                Logger.getLogger(TileSetWidget.class).error(ex);
            }
        }
    }
}
